package puzzle.akari.solver;

import puzzle.akari.data.AkariBoard;
import puzzle.akari.data.BlockNumber;
import puzzle.common.data.HatanException;
import puzzle.common.point.Direction;
import puzzle.common.point.Pnt;
import puzzle.common.point.PntHelper;

class NumberRule extends AkariCellRule {

	@Override
	protected boolean apply(AkariBoard b, int x, int y) throws HatanException {
		int n = b.getBlockNumber(x, y);
		if (!BlockNumber.isNumber(n)) {
			return false;
		}

		Pnt[] ps = PntHelper.getDirectNeighborPointsAround(x, y);

		int forbiddenCount = 0;
		int putCount = 0;
		int possibleCount = 0;
		for (Pnt p : ps) {
			if (b.isLightForbidden(p)) forbiddenCount++;
			if (b.isLightPut(p)) putCount++;
			if (b.canLightBePut(p)) possibleCount++;
		}

		if (forbiddenCount + putCount == 4) {
			return false;
		}

		boolean isChanged = false;

		if (putCount == n) {
			for (Pnt p : ps) {
				if (b.canLightBePut(p)) {
					isChanged |= b.setLightForbidden(p);
				}
			}
		} else if (forbiddenCount == 4 - n) {
			for (Pnt p : ps) {
				if (b.canLightBePut(p)) {
					isChanged |= b.setLightPut(p);
				}
			}
		}

		if (putCount + possibleCount - 1 == n) {
			isChanged |= applyCornerRule(b, x, y, Direction.NE);
			isChanged |= applyCornerRule(b, x, y, Direction.SE);
			isChanged |= applyCornerRule(b, x, y, Direction.SW);
			isChanged |= applyCornerRule(b, x, y, Direction.NW);
		}

		return isChanged;
	}

	private boolean applyCornerRule(AkariBoard b, int x, int y, Direction d) throws HatanException {
		int x1 = x + d.v.dx;
		int y1 = y + d.v.dy;
		boolean isChanged = false;
		if (b.canLightBePut(x1, y) && b.canLightBePut(x, y1)) {
			int n = b.getBlockNumber(x1, y1);
			if (BlockNumber.isNonBlock(n)) {
				isChanged |= b.setLightForbidden(x + d.v.dx, y + d.v.dy);
			} else if (BlockNumber.isNumber(n)) {
				switch (n) {
				case 1:
					isChanged |= b.setLightForbidden(x1 + d.v.dx, y1);
					isChanged |= b.setLightForbidden(x1, y1 + d.v.dy);
					break;
				case 2:
					if (b.isLightForbidden(x - d.v.dx, y) && b.isLightForbidden(x, y - d.v.dy)) {
						Pnt p1 = new Pnt(x1 + d.v.dx, y1);
						Pnt p2 = new Pnt(x1, y1 + d.v.dy);
						isChanged |= selectEitherOne(b, p1, p2);
					}
					break;
				case 3:
					if (b.getBlockNumber(x, y) == 1) {
						isChanged |= b.setLightPut(x1 + d.v.dx, y1);
						isChanged |= b.setLightPut(x1, y1 + d.v.dy);
					}
					break;
				}
			}
		}
		return isChanged;
	}

	private boolean selectEitherOne(AkariBoard b, Pnt p1, Pnt p2) throws HatanException {
		boolean isChanged = false;
		if (b.isLightForbidden(p1)) {
			isChanged |= b.setLightPut(p2);
		} else if (b.isLightForbidden(p2)) {
			isChanged |= b.setLightPut(p1);
		}
		return isChanged;
	}

}
